package com.shungen.realapigateway.config;

import com.shungen.realapigateway.common.ErrorCode;
import com.shungen.realapigateway.exception.ThrowUtils;
import com.shungen.realapigateway.model.entity.InterfaceInfo;
import com.shungen.realapigateway.model.entity.PointTransaction;
import com.shungen.realapigateway.model.entity.User;
import com.shungen.realapigateway.service.AuthService;
import com.shungen.realapigateway.service.InterfaceInfoService;
import com.shungen.realapigateway.service.NonceService;
import com.shungen.realapigateway.service.PointTransactionService;
import com.shungen.realapigateway.utils.SignUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.cloud.gateway.support.ServerWebExchangeUtils;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.util.UriComponentsBuilder;
import reactor.core.publisher.Mono;

import javax.annotation.Resource;
import java.net.URI;

/**
 * @author shungen
 * @date 2024年08月10日 12:04
 */
@Component
@Order(1)
@Slf4j
@Transactional(propagation = Propagation.REQUIRES_NEW)
public class AuthenticationFilter implements GlobalFilter {

    @Resource
    private AuthService authService;  // 认证服务

    @Resource
    private InterfaceInfoService interfaceInfoService;


    @Resource
    private PointTransactionService pointTransactionService;

    @Resource
    private NonceService nonceService;

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        // 1. 请求日志
        ServerHttpRequest request = exchange.getRequest();
        String method = request.getMethod().toString();
        log.info("请求唯一标识：" + request.getId());
        log.info("请求方法：" + method);
        log.info("请求参数：" + request.getQueryParams());
        String sourceAddress = request.getLocalAddress().getHostString();
        log.info("请求来源地址：" + sourceAddress);
        log.info("请求来源地址：" + request.getRemoteAddress());
        ServerHttpResponse response = exchange.getResponse();


        // 记录原始请求的URL
        String originalUrl = request.getURI().toString();
        log.info("原始请求URL：" + originalUrl);
        // 获取待转发的URL
//        URI targetUri = exchange.getAttribute("org.springframework.cloud.gateway.support.ServerWebExchangeUtils.gatewayRequestUrl");
//        if (targetUri != null) {
//            log.info("待转发的目标URL：" + targetUri.toString());
//        } else {
//            log.warn("未找到待转发的目标URL，可能是路由映射的问题");
//        }

        // 2. 用户鉴权（判断 ak、sk 是否合法）
        HttpHeaders headers = request.getHeaders();
        String accessKey = headers.getFirst("accessKey");
        String nonce = headers.getFirst("nonce");
        String timestamp = headers.getFirst("timestamp");
        String signature = headers.getFirst("signature");



        User user = authService.getUserByAccessKey(accessKey);
        // 3.1 获取 secretKey
        String secretKey = user.getSecretKey();
        if (secretKey == null) {
            response.setStatusCode(HttpStatus.FORBIDDEN);
            return response.setComplete(); // 访问秘钥无效
        }

        // 3.2 验证时间戳是否在有效期内（例如2分钟内）
        long currentTimeMillis = System.currentTimeMillis() / 1000;
        long requestTimeMillis;
        try {
            requestTimeMillis = Long.parseLong(timestamp);
        } catch (NumberFormatException e) {
            response.setStatusCode(HttpStatus.BAD_REQUEST);
            return response.setComplete(); // 时间戳格式错误
        }

        if (Math.abs(currentTimeMillis - requestTimeMillis) > 2 * 60) {
            response.setStatusCode(HttpStatus.UNAUTHORIZED);
            return response.setComplete(); // 请求过期
        }

        // 3.3 验证 nonce 是否已被使用
        if (nonceService.isNonceUsed(nonce)) {
            response.setStatusCode(HttpStatus.UNAUTHORIZED);
            return response.setComplete(); // Nonce 重复使用
        }
        nonceService.saveNonce(nonce); // 将 nonce 标记为已使用

        // 4. 生成服务器端签名（仅使用 nonce 和时间戳）
        String signData = nonce + timestamp;
        String serverSignature = SignUtils.generateSignature(signData, secretKey);

        // 5. 比较签名
        if (!serverSignature.equals(signature)) {
            response.setStatusCode(HttpStatus.UNAUTHORIZED);
            return response.setComplete(); // 签名不匹配
        }


        //6.从断言中获取接口id
//        String path = request.getPath().toString();
//        System.out.println(path);
//        Long interfaceId = Long.valueOf(path.split("/")[2]); // 根据实际路径调整
        Long interfaceId = Long.valueOf(headers.getFirst("interfaceId"));//将接口id存放在请求头里面，以免对路由映射造成麻烦
        InterfaceInfo interfaceInfo = interfaceInfoService.getInterfaceInfoById(interfaceId);
        Long price = interfaceInfo.getPrice();
        Long pointBalance = user.getPointBalance();
        if(pointBalance >= price){
            //允许访问,先扣除积分
            boolean subResult = authService.subUserPoint(user.getId(), price);
            System.out.println(subResult);
//            ThrowUtils.throwIf(!subResult, ErrorCode.FORBIDDEN_ERROR,"积分扣除失败");
            if(!subResult && price != 0){
                log.error("用户id:" + user.getId() + "积分扣除失败");
            }
            //添加积分变动记录
            PointTransaction newPointTransaction = new PointTransaction();
            newPointTransaction.setUserId(user.getId());
            newPointTransaction.setDescription("接口调用消耗");
            newPointTransaction.setChangeAmount(-price);
            boolean pointResult = pointTransactionService.addPointTransaction(newPointTransaction);
//            ThrowUtils.throwIf(!pointResult, ErrorCode.FORBIDDEN_ERROR,"积分扣除记录增加失败");
            if(!pointResult && price != 0 ){
                log.error("用户id:" + user.getId() + "积分扣除记录增加失败");
            }


            // 打印日志
            log.info("访问用户:{" + user.getUserAccount() + "},访问接口:{接口id:" + interfaceId + "},用户积分余额:" + user.getPointBalance() + ",接口消耗积分:" + interfaceInfo.getPrice());



            // 转发请求
            // 调用下一个过滤器

            //访问次数+1
            interfaceInfoService.incrementVisitedCount(interfaceId);
            //TODO  用户的访问记录+1

            return chain.filter(exchange).then(Mono.fromRunnable(() -> {
                // 获取并输出目标地址
                String originalUri = exchange.getRequest().getURI().toString();
                String routeUri = exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR).toString();
                log.info("Original URI: {}", originalUri);
                log.info("Forwarding to URI: {}", routeUri);
            }));

        }else{
            response.setStatusCode(HttpStatus.UNAUTHORIZED);
            return response.setComplete(); // 积分不足
        }

    }
}
